home *** CD-ROM | disk | FTP | other *** search
/ Aminet 4 / Aminet 4 - November 1994.iso / aminet / util / misc / timeplanner.lha / TimePlanner / tp.c < prev   
C/C++ Source or Header  |  1994-05-29  |  27KB  |  936 lines

  1. //----------------------------------------------------------------------------
  2. // TimePlanner V1.0                                        © 1993 L. Vanhelsuwé
  3. // ----------------                                        --------------------
  4. // Background deamon for starting periodic background processes or for reminding
  5. // users of appointments (birthdays, car insurance, ...).
  6. // Uses an Events file to find out when to become active and notify user or to
  7. // execute secondary batch processes.
  8. //
  9. // BUGS:
  10. // -----
  11. // -Program is not localization-proof. When locale is not English : corruption
  12. //  occurs in EV.LST
  13. //
  14. // History:
  15. // --------
  16. // Sat 24-JAN-93: started this file.
  17. // Sun 31-JAN-93: finished basic useful version
  18. // Sun 09-MAY-93: added early reminder "X DAYS/HOURS/MINUTES to go before.."
  19. // Mon 10-MAY-93: added ENV:EV.LST marking to find out where we're waiting
  20. //          bumped revision to 1.1
  21. // Tue 18-MAY-93: added graceful exit for 1.3 users.
  22. // Fri 21-JAN-94: implemented EXECUTE option to execute batch files or commands
  23. //                  added delay after initial loading for faster/clearer startup
  24. //
  25. //----------------------------------------------------------------------------
  26.  
  27. #include    <stdio.h>
  28. #include    <string.h>
  29. #include    <stdlib.h>
  30.  
  31. #ifdef    AMIGA
  32.  
  33. #include    <exec/types.h>
  34. #include    <exec/memory.h>
  35. #include    <dos/dos.h>
  36. #include    <dos/datetime.h>
  37. #include    <intuition/screens.h>
  38. #include    <intuition/intuitionbase.h>
  39. #include    <graphics/gfx.h>
  40.  
  41. #include    <clib/exec_protos.h>            // ANSI function prototypes
  42. #include    <clib/alib_protos.h>
  43. #include    <clib/dos_protos.h>
  44. #include    <clib/graphics_protos.h>
  45. #include    <clib/intuition_protos.h>
  46.  
  47. #endif
  48.  
  49. #define    MAX_REMINDERS    (12)            // upto N-1 early reminders
  50.  
  51. #define    COLON        (':')
  52. #define    SEMICOLON    (';')
  53. #define    COMMA        (',')
  54. #define    HYPHEN        ('-')
  55. #define    QUOTE        ('"')
  56. #define    VERT_BAR    ('|')
  57. #define    TAB            (9)
  58. #define    LF            (10)
  59. #define    ASTERISK    ('*')
  60. #define    SPACE        (32)
  61.  
  62. #define    MINUTETICKS    (60*50)                // 50 Amiga TICKS in a second
  63. #define    HOURTICKS    (60*MINUTETICKS)
  64. #define    DAYTICKS    (24*HOURTICKS)
  65. #define    WEEKTICKS    (7*DAYTICKS)
  66. #define    MONTHTICKS    (30*DAYTICKS)
  67.  
  68. #define    NUM_DAYS    (7)
  69. #define    NUM_MONTHS    (12)
  70.  
  71. #define    BUT_OK        (1)
  72. #define    BUT_CANCEL    (2)
  73. //-----------------------------------------------------------------------------
  74. // Function prototypes
  75. // -------------------
  76.  
  77. void    parse_user_events    ( void );
  78. void    skip_line            ( void );
  79. void    skip_whitespc        ( void );
  80. void    print_line            ( void );
  81. char    popup_requester        ( char *msg, char buttons);
  82. void    make_exe_file        ( void );
  83. void    process_line        ( void );
  84. void    process_on_line        ( void );
  85. void    init_TP                ( void );
  86. void    adjust                ( struct DateStamp *ds, ULONG ticks);
  87. void    handle_events        ( void );
  88. void    mark_file            ( char *file, int position, char *tag);
  89. void    do_event            ( char *req_msg, char *batch_file);
  90.  
  91. char    * load_file            ( char * filename);
  92.  
  93. BOOL    is_string            ( char * txt, char * string);
  94. BOOL    DStampToStr            ( struct DateStamp *ds, char *daystr, char *datestr, char *timestr);
  95.  
  96. short    get_num                ( void );
  97. short    get_month            ( void );
  98. short    get_dayname            ( void );
  99. short    process_remind_line    ( void );
  100. short    month_from_str        ( char *month );
  101.  
  102. //-----------------------------------------------------------------------------
  103. // Global statics
  104. // --------------
  105.  
  106. struct    IntuitionBase *IntuitionBase;        // for access to ActiveWindow
  107.  
  108. struct    DateStamp    datestamp;                // for DateStamp(), DateToStr(),..
  109. struct    DateTime    datetime;
  110.  
  111. char    day_str[16];                        // Wednesday is biggest
  112. char    date_str[10];                        // 24-JAN-93
  113. char    time_str[10];                        // 21:47:47
  114.  
  115. char    *file_buf, *events_file;
  116. char    *txt;                                // text pointer (into buffer)
  117. ULONG    file_size;
  118.  
  119. struct REM                            // REMINDER structure
  120. {
  121.     struct DateStamp    REM_ds;
  122.     char                REM_string[80];
  123. };
  124.  
  125. struct REM reminders[MAX_REMINDERS];        // 0..N reminder dates/times
  126.  
  127. char    month_codes[]="0123456789AB";        // month codes
  128.  
  129. char    *months[]={"JAN","FEB","MAR","APR","MAY","JUN",
  130.                    "JUL","AUG","SEP","OCT","NOV","DEC"};
  131.  
  132. char    *days[]={"MON","TUE","WED","THU","FRI","SAT","SUN"};
  133.  
  134. char    *version = "$VER: TimePlanner 1.0 ©LVA 22/JAN/94\n";
  135.  
  136. FILE    *fh;                                // file handle
  137.  
  138. BOOL    ok;
  139.  
  140. int        i;
  141.  
  142. //-----------------------------------------------------------------------------
  143. // outline of program:
  144. // - from user events file, create a sorted formalized version
  145. // - process all future events by waiting for event time, then executing action
  146. //-----------------------------------------------------------------------------
  147.  
  148. void main (void) {
  149.  
  150. struct DosBase * libptr;
  151.  
  152.     printf("TimePlanner (TM) V1.0\n");
  153.     printf("Designed and implemented by Laurence Vanhelsuwé © Jan 1993\n\n");
  154.  
  155.     libptr = ( struct DosBase*) OpenLibrary("dos.library", 37);    // check for 2.0 OS
  156.  
  157.     if (libptr) {
  158.         CloseLibrary((struct Library*)libptr);
  159.  
  160.         Delay(5*50);                            // let startup-sequence complete
  161.  
  162.         init_TP();                                // initialize things
  163.  
  164.         events_file = load_file("S:EVENTS");    // cache user events file
  165.  
  166.         parse_user_events();                    // generate formalised events list
  167.  
  168.         FreeMem(file_buf, file_size);            // free user events file buffer
  169.  
  170.         Execute("Sort ENV:ev.raw TO ENV:ev.lst", NULL, NULL);    // sort events on date
  171.         DeleteFile("ENV:ev.raw");                // delete unsorted file
  172.  
  173.         handle_events();                        // process queue, waiting for right times..
  174.  
  175.         FreeMem(file_buf, file_size);            // free events file buffer
  176.     } else {
  177.         printf("TimePlanner needs AmigaDOS 2.0 (V37+)...Sorry!\n");
  178.     }
  179.  
  180.     printf("TimePlanner quitting. Have a nice day.\n");
  181. }
  182.  
  183. //-----------------------------------------------------------------------------
  184. // Initialize program.
  185. //-----------------------------------------------------------------------------
  186. void init_TP (void) {
  187.  
  188.     DateStamp(&datestamp);
  189.     ok = DStampToStr(&datestamp, day_str, date_str, time_str);
  190.     printf("Today is %s %s and the time is %s\n", day_str, date_str, time_str);
  191.  
  192.     IntuitionBase = (struct IntuitionBase*) OpenLibrary("intuition.library", 36);
  193.     if (!IntuitionBase) {
  194.         printf("TimePlanner ERROR: No IntuitionBase !\n");
  195.         exit(100);
  196.     }
  197. }
  198. //-----------------------------------------------------------------------------
  199. // Scan through sorted events list, waiting for next event and then executing
  200. // the required action.
  201. //
  202. // Example lines:
  203. //
  204. // 1994 0 22 15:55:00 R 5 Minutes to go before...Jonah !|
  205. // 1994 0 22 16:00:00 N Jonah !|
  206. //
  207. //-----------------------------------------------------------------------------
  208. void handle_events( void ) {
  209.  
  210. unsigned int delay,offset;
  211. short    day,month,year,hour,minute;
  212. ULONG    tmin_days,tmin_mins,tmin_ticks;        // T-minus X days, minutes, ticks
  213. char     ch;
  214. char    * req_msg;                    // pointer to text for requester
  215. char    * file_end;
  216. char    datestring[20], timestring[20];
  217. char    * batch_file;                        // don't execute anything
  218. static    char reminder_msg[256];
  219.  
  220.     txt = events_file = load_file("ENV:EV.LST");    // load sorted events file
  221.  
  222.     file_end = txt + file_size;                    // calculate EOF in buffer
  223.  
  224.     while (txt < file_end) {                    // while not past EOF
  225.  
  226.         req_msg    = NULL;
  227.         batch_file        = NULL;                    // assume no BATCH file
  228.  
  229.         offset = txt - events_file;
  230.         mark_file ("ENV:EV.LST", offset, ">");    // label line we're waiting on
  231.  
  232.         year    = get_num(); txt++;                // get event date and time...
  233.         month    = (*txt) < 'A' ? (*txt)-'0' : (*txt)-'A'+10;
  234.         txt +=2;
  235.         day        = get_num(); txt++;                // skip space
  236.         hour    = get_num(); txt++;                // skip COLON
  237.         minute    = get_num(); txt++;                // skip COLON
  238.         get_num();                                // discard seconds field
  239.  
  240.         txt++;                                    // skip space char
  241.  
  242.         ch = *txt++;
  243.  
  244.         switch (ch) {
  245.             case 'N':    txt++;                    // skip space
  246.     
  247.                         req_msg = txt;                // notif. msg is user's string
  248.     
  249.                         while (*txt++ != VERT_BAR) ;
  250.                         *(txt-1) = 0;            // turn into C-string
  251.     
  252.                         if (*txt == LF) break;
  253.  
  254.                         txt++;                    // skip space
  255.                         if (*txt++ == 'E') {
  256.                             txt++;                // skip space
  257.                             txt++;                // skip filename quote
  258.                             batch_file = txt;
  259.                             while (*txt != QUOTE) txt++;
  260.                             *txt++ = 0;            // turn filname into C-string
  261.                         }
  262.                         break;
  263.     
  264.             case 'R':    txt++;                    // skip space
  265.     
  266.                         req_msg = txt;
  267.     
  268.                         while (*txt++ != VERT_BAR) ;
  269.                         *(txt-1) = 0;            // turn into C-string
  270.     
  271.                         sprintf(reminder_msg, "EARLY REMINDER MESSAGE:\n\n");
  272.                         sprintf(reminder_msg+24, req_msg);
  273.                         req_msg = reminder_msg;
  274.     
  275.                         break;
  276.     
  277.             case 'E':    txt++;                    // skip space
  278.  
  279.                         txt++;                    // skip filename quote
  280.                         batch_file = txt;
  281.                         while (*txt != QUOTE) txt++;
  282.                         *txt++ = 0;                // turn filname into C-string
  283.  
  284.                         break;
  285.         }
  286.  
  287.         skip_line();            // discard any comments after command
  288.  
  289. //printf("READY TO ROLL ? %4d %c %02d %02d:%02d\n", year, month_codes[month], day, hour, minute);
  290.  
  291.     // create date & time strings for submission to StrToDate()
  292.         sprintf(datestring, "%02d-%s-%02d",day, months[month], year-1900);
  293.         sprintf(timestring, "%02d:%02d", hour, minute);
  294.  
  295.         datetime.dat_Format    = FORMAT_DOS;
  296.         datetime.dat_StrDate= datestring;
  297.         datetime.dat_StrTime= timestring;
  298.         datetime.dat_Flags    = 0;
  299.         StrToDate(&datetime);
  300.  
  301.         DateStamp(&datestamp);                // find out what day/time is now
  302.  
  303.         if (hour == 0 && minute == 0) {        // if event doesn't specify a time
  304.                                             // check whether event happens today
  305.             if (datestamp.ds_Days == datetime.dat_Stamp.ds_Days)
  306.                 do_event(req_msg, batch_file);
  307.         }
  308.  
  309.     // check whether event is in past or future
  310.         if (CompareDates(&datestamp, &datetime.dat_Stamp) > 0) {
  311.  
  312.         // calculate # of Ticks before event fires.
  313.             tmin_days    = datetime.dat_Stamp.ds_Days    - datestamp.ds_Days;
  314.             tmin_mins    = datetime.dat_Stamp.ds_Minute    - datestamp.ds_Minute;
  315.             tmin_ticks    = datetime.dat_Stamp.ds_Tick    - datestamp.ds_Tick;
  316.  
  317.             printf ("%d Days, %d Minutes and %d Ticks to event!\n",
  318.                 tmin_days, tmin_mins, tmin_ticks);
  319.             delay = tmin_ticks + MINUTETICKS*tmin_mins + DAYTICKS*tmin_days;
  320.  
  321.             printf("Delay(%d) !\n", delay);
  322.             Delay(delay);                    // Sleep until event should happen
  323.  
  324.             do_event(req_msg, batch_file);    // BANG !!
  325.         }
  326.  
  327.         mark_file ("ENV:EV.LST", offset, " ");    // label line as "processed"
  328.  
  329.     }        // WHILE NOT EOF
  330. }
  331. //-----------------------------------------------------------------------------
  332. // An event's time has come... do it !
  333. //-----------------------------------------------------------------------------
  334.  
  335. void do_event( char *req_msg, char *batch_file) {
  336. char choice;
  337. BOOL batch_ok;
  338. char str[256];
  339.  
  340.  
  341.     batch_ok = TRUE;                // assume no problems with batch file.
  342.  
  343.     if (req_msg) {
  344.         if (batch_file) {
  345.             choice = popup_requester(req_msg, BUT_OK|BUT_CANCEL);    // Notify user of event.
  346.             if (choice==BUT_OK)
  347.                 batch_ok = Execute(batch_file, NULL, NULL);
  348.         } else {
  349.             choice = popup_requester(req_msg, BUT_OK);
  350.         }
  351.     } else if (batch_file)
  352.                 batch_ok = Execute(batch_file, NULL, NULL);
  353.  
  354.     if(! batch_ok) {
  355.         sprintf(str, "Batch file '%s' couldn't be started !\n", batch_file);
  356.         popup_requester(str, BUT_OK);
  357.     }
  358. }
  359.  
  360. //-----------------------------------------------------------------------------
  361. //-----------------------------------------------------------------------------
  362. // Parse USER events file and generate a formal list to be sorted on date.
  363. //-----------------------------------------------------------------------------
  364. void parse_user_events( void ) {
  365.  
  366. char firstchar;
  367. char * file_end;
  368.  
  369.     fh = fopen("ENV:ev.raw","w");    // create formal file
  370.  
  371.     txt = events_file;                // starting from start of user file...
  372.  
  373.     file_end = txt + file_size;        // calculate EOF in buffer
  374.  
  375.     while (txt < file_end) {
  376.  
  377. // print_line();    **!! DEBUG
  378.  
  379.         firstchar = *txt;
  380.  
  381.         switch (firstchar) {
  382.             case SEMICOLON:
  383.             case ASTERISK:    skip_line(); break;        // skip comment lines
  384.  
  385.             case LF:        txt++; break;            // skip empty lines
  386.  
  387.             case TAB:
  388.             case SPACE:        skip_whitespc(); break;    // skip leading indentation
  389.  
  390.             default:        process_line();            // process command
  391.                             skip_line(); break;        // and discard any comments
  392.         }
  393.     }
  394.  
  395.     fclose(fh);
  396. }
  397. //-----------------------------------------------------------------------------
  398. // Process a line from the USER events file.
  399. // Output one/many formalised version(s) for later interpretation.
  400. //-----------------------------------------------------------------------------
  401. void process_line(void) {
  402.  
  403. short actions=1;                    // default number of events
  404. char *notify_str, *execute_str;
  405. short year,month,day;
  406.  
  407.  
  408. // Look for the start of a TimePlanner Event entry.
  409. // These all start with an "ON ...." line
  410.  
  411.     if (is_string(txt,"ON")) {
  412.  
  413.         notify_str = execute_str = NULL;    // invalidate str ptrs.
  414.  
  415.         process_on_line();                    // get date-time as Ticks values
  416.         skip_line(); skip_whitespc();        // goto next line (junk poss. comment)
  417.  
  418. // Now check for any of the possible actions to do ON that date/time
  419. // First a possible REMINDer line.
  420.  
  421.         if (is_string(txt,"REMIND")) {
  422.             actions = process_remind_line();
  423.             skip_line();                        // trash any remainder
  424.             skip_whitespc();                    // skip indent on next line
  425.         }
  426.  
  427. // Then the NOTIFY line.
  428.  
  429.         if (is_string(txt,"NOTIFY")) {
  430.  
  431.             txt += 6; skip_whitespc();
  432.  
  433.             if(*txt != QUOTE) {
  434.                 printf("No QUOTE after NOTIFY Command.\n");
  435.                 exit(10);
  436.             }
  437.  
  438.             notify_str = ++txt;                    // notify str starts past quote
  439.  
  440.             while (*txt != LF)                    // find end of notify string
  441.                 txt++;
  442.  
  443.             *txt = 0;                            // turn notif. str to C-string
  444.  
  445.             if (*(txt-1) == QUOTE)
  446.                 *(txt-1) = 0;            // and strip closing quote if present
  447.  
  448.             txt++;                                // goto next line
  449.             skip_whitespc();                    // skip indent on next line
  450.         }
  451.  
  452. // Then an optional EXECUTE line.
  453.  
  454.         if (is_string(txt,"EXECUTE")) {
  455.             txt += 7; skip_whitespc();    // skip white space between cmd and arg
  456.  
  457.             if(*txt != QUOTE) {
  458.                 printf("No QUOTE after EXECUTE Command.\n");
  459.                 exit(10);
  460.             }
  461.  
  462.             execute_str = txt;                    // execute str INCLUDES quote
  463.  
  464.             while (*txt != LF)                    // find end of notify string
  465.                 txt++;
  466.  
  467.             *txt = 0;                            // turn notif. str to C-string
  468.             skip_whitespc();                    // skip indent on next line
  469.         }
  470.  
  471. // If there's no NOTIFY or EXECUTE to do for this Event : ERROR !
  472.  
  473.         if (notify_str == NULL && execute_str == NULL) {
  474.             printf("Error: ON line not followed by any action!... Aborting.\n");
  475.             exit(10);
  476.         }
  477.  
  478. // Now write out N lines (1 minimum, more if reminders)
  479.  
  480.         for (i=0; i<actions; i++) {
  481.  
  482.             ok = DStampToStr(&reminders[i].REM_ds, day_str, date_str, time_str);
  483.     //        printf("Reminder[%d] = %s %s %s  ", i, day_str, date_str, time_str);
  484.  
  485.             year = 1900 + 10*(date_str[7]-'0') + date_str[8] -'0';
  486.             month = month_from_str(date_str+3);
  487.             day = 10*(date_str[0]-'0') + date_str[1] - '0';
  488.  
  489.     //    printf("OUTPUT: %4d %c %02d %s\n", year, month_codes[month], day, time_str);
  490.             fprintf(fh, "%4d %c %02d %s", year, month_codes[month], day, time_str);
  491.  
  492.             if (notify_str) {
  493.  
  494.                 if (i==0) {
  495.                     fprintf( fh, " N ");    // the first one is main event
  496.                 } else {
  497.                     fprintf( fh, " R ");    // all others are reminders
  498.                     fprintf( fh, reminders[i].REM_string);    // incl. CR
  499.                 }
  500.  
  501.                 fprintf( fh, notify_str);
  502.                 fprintf( fh, "|");            // VERT_BAR ends notify str.
  503.  
  504.                 if (execute_str && i==0) {
  505.                     fprintf( fh, " E ");
  506.                     fprintf( fh, execute_str);
  507.                 }
  508.             } else {
  509.                 if (execute_str) {
  510.                     fprintf( fh, " E ");
  511.                     fprintf( fh, execute_str);
  512.                 }
  513.             }
  514.  
  515.             fprintf( fh, "\n");                // terminate line in generated file
  516.         }    // FOR
  517.  
  518.     }        // IF (IS_STRING("ON"))
  519. }            // end of function
  520. //-----------------------------------------------------------------------------
  521. // Process a line starting with the "ON" keyword.
  522. // e.g.
  523. // ON TUE 16-JAN-93 AT 12:00
  524. // ON 22-MAR-87 AT 22:00
  525. // ON 01-JAN-99
  526. //-----------------------------------------------------------------------------
  527. void process_on_line (void) {
  528.  
  529. short day,month,year;
  530. short hour=0,minute=0;            // default time specification 00:00 (invalid)
  531. char ch;
  532. char *datestr, *timestr;
  533.  
  534.         txt += 2; skip_whitespc();            // skip over "ON" chars
  535.  
  536.         if (get_dayname() != -1) {            // skip over optional day name
  537.             txt += 3;
  538.         }
  539.         skip_whitespc();
  540.  
  541.         datestr = txt;                        // remember where date starts.
  542.  
  543.         day = get_num();                    // parse date DD-MMM-YYYY
  544.         if (day<1 || day >31) printf("Error in day number in ON statement.\n");
  545.         if (*txt++ != '-') printf("Error: no hyphen after day number.\n");
  546.  
  547.         month = get_month();                // JAN, FEB, MAR, ...
  548.         if (month == -1) printf("Error in month in ON statement.\n");
  549.         if (*txt++ != '-') printf("Error: no hyphen after month.\n");
  550.  
  551.         year = get_num();
  552.         if (year <1978 || year> 2077) printf("Error in year in ON statement.\n");
  553.         ch = *txt;
  554.  
  555.         datestr[7] = datestr[9];        // turn -YYYY into -YY format
  556.         datestr[8] = datestr[10];
  557.         datestr[9] = 0;                    // turn date into C-string
  558.  
  559.         skip_whitespc();
  560.  
  561.         ch = *txt; timestr = NULL;
  562.         if (ch != LF) {                        // check optional time specification
  563.             if (is_string(txt,"AT")) {
  564.                 txt += 2; skip_whitespc();    // skip over "AT" chars
  565.  
  566.                 timestr = txt;                // remember where timestr starts
  567.  
  568.                 hour = get_num(); if(hour<0 || hour>23) printf("Error in hour\n");
  569.                 if (*txt++ != COLON) printf("Error: no colon in time.\n");
  570.  
  571.                 minute = get_num(); if (minute<0 || minute >59) printf("Error in minutes\n");
  572.  
  573.                 ch = timestr[5];            // remember what we're overwriting
  574.                 timestr[5] = 0;                // turn time into C-string
  575.             }
  576.         }
  577.  
  578.         datetime.dat_Format    = FORMAT_DOS;
  579.         datetime.dat_StrDate= datestr;
  580.         datetime.dat_StrTime= timestr;
  581.         datetime.dat_Flags    = 0;
  582.         StrToDate(&datetime);
  583.  
  584.         if (!timestr) {                        // if no time was specified:
  585.             datetime.dat_Stamp.ds_Minute = 0;    // set time to 00:00
  586.             datetime.dat_Stamp.ds_Tick    = 0;
  587.         } else {
  588.             timestr[5] = ch;                // restore char in line
  589.         }
  590.  
  591.         for (i=0; i< MAX_REMINDERS; i++) {
  592.             reminders[i].REM_ds = datetime.dat_Stamp;
  593.         }
  594. }
  595. //-----------------------------------------------------------------------------
  596. // Process a line starting with the "REMIND" keyword.
  597. // e.g. REMIND MONTH, DAY, HOUR, x DAYS, x HOURS, x MINUTES
  598. //-----------------------------------------------------------------------------
  599. short process_remind_line (void) {
  600.  
  601. short times=1;            // array index
  602. int units;
  603. BOOL theres_more=TRUE;
  604.  
  605.     txt += 6; skip_whitespc();            // skip over "REMIND" chars
  606.  
  607.     while (theres_more && times < MAX_REMINDERS) {
  608.  
  609.         if (is_string(txt, "MONTH")) {
  610.             strcpy( reminders[times].REM_string, "1 Month to go before...");
  611.             adjust ( &reminders[times++].REM_ds, MONTHTICKS);
  612.             txt += 5;                                // skip over "MONTH" keyword
  613.  
  614.             if (*txt == COMMA) {                    // check if there are more
  615.                 txt++; skip_whitespc();                // reminders
  616.             } else {
  617.                 theres_more = FALSE;
  618.                 continue;                                // goto WHILE
  619.             }
  620.         }
  621.  
  622.         if (is_string(txt, "DAY")) {
  623.             strcpy( reminders[times].REM_string, "1 Day to go before...");
  624.             adjust ( &reminders[times++].REM_ds, DAYTICKS);
  625.             txt += 3;                                // skip over "DAY" keyword
  626.  
  627.             if (*txt == COMMA) {
  628.                 txt++; skip_whitespc();
  629.             } else {
  630.                 theres_more = FALSE;
  631.                 continue;
  632.             }
  633.         }
  634.  
  635.         if (is_string(txt, "HOUR")) {
  636.             strcpy( reminders[times].REM_string, "1 Hour to go before...");
  637.             adjust ( &reminders[times++].REM_ds, HOURTICKS);
  638.             txt += 4;                                // skip over "HOUR" keyword
  639.  
  640.             if (*txt == COMMA) {
  641.                 txt++; skip_whitespc();
  642.             } else {
  643.                 theres_more = FALSE;
  644.                 continue;
  645.             }
  646.         }
  647.  
  648.         if (units = get_num()) {            // if a non-zero number at the start
  649.             skip_whitespc();                            // skip over space
  650.  
  651.             if (is_string(txt, "DAYS")) {
  652.                 sprintf(reminders[times].REM_string, "%d Days to go before...", units);
  653.                 adjust ( &reminders[times++].REM_ds, DAYTICKS * units);
  654.                 txt += 4; skip_whitespc();
  655.  
  656.                 if (*txt == COMMA) {
  657.                     txt++; skip_whitespc();
  658.                 } else {
  659.                     theres_more = FALSE;
  660.                     continue;
  661.                 }
  662.             }
  663.  
  664.             if (is_string(txt, "HOURS")) {
  665.                 sprintf(reminders[times].REM_string, "%d Hours to go before...", units);
  666.                 adjust ( &reminders[times++].REM_ds, HOURTICKS * units);
  667.                 txt += 5; skip_whitespc();
  668.  
  669.                 if (*txt == COMMA) {
  670.                     txt++; skip_whitespc();
  671.                 } else {
  672.                     theres_more = FALSE;
  673.                     continue;
  674.                 }
  675.             }
  676.  
  677.             if (is_string(txt, "MINUTES")) {
  678.                 sprintf(reminders[times].REM_string, "%d Minutes to go before...", units);
  679.                 adjust ( &reminders[times++].REM_ds, MINUTETICKS * units);
  680.                 txt += 7; skip_whitespc();
  681.  
  682.                 if (*txt == COMMA) {
  683.                     txt++; skip_whitespc();
  684.                 } else {
  685.                     theres_more = FALSE;
  686.                     continue;
  687.                 }
  688.             }
  689.         }        // IF (GETNUM)
  690.     }            // WHILE 
  691.  
  692.     return times;            // tell how many reminders to generate
  693. }
  694. //-----------------------------------------------------------------------------
  695. // Modify a DateStamp by a number of Ticks.
  696. //-----------------------------------------------------------------------------
  697. void adjust (struct DateStamp *ds, ULONG ticks) {
  698.  
  699. int dd,dm,dt;
  700.  
  701. //printf("Ticks to adjust: %d\n", ticks);
  702. //printf("BEFORE ADJUSTED Days, Minutes, Ticks: %d, %d, %d\n", 
  703. //    ds->ds_Days, ds->ds_Minute, ds->ds_Tick);
  704.  
  705.     dd = ticks/DAYTICKS;    ticks = ticks%DAYTICKS;
  706.     dm = ticks/MINUTETICKS;
  707.     dt = ticks%MINUTETICKS;
  708.  
  709. //printf("Adjustments: %d, %d, %d\n", dd, dm, dt);
  710.  
  711.     ds->ds_Days        -= dd;
  712.     ds->ds_Minute    -= dm;
  713.     ds->ds_Tick        -= dt;
  714.  
  715.     if(ds->ds_Tick < 0) {
  716.         ds->ds_Tick += MINUTETICKS;            // make positive again
  717.         ds->ds_Minute--;                    // but adjust minutes
  718.     }
  719.  
  720.     if (ds->ds_Minute < 0) {
  721.         ds->ds_Minute += 60*60*24;
  722.         ds->ds_Days--;
  723.     }
  724. //printf("ADJUSTED Days, Minutes, Ticks: %d, %d, %d\n", 
  725. //    ds->ds_Days, ds->ds_Minute, ds->ds_Tick);
  726. }
  727. //-----------------------------------------------------------------------------
  728. // Get an embedded decimal number. Return 0 if no number !
  729. //-----------------------------------------------------------------------------
  730. short get_num(void) {
  731.  
  732. short num=0;
  733.  
  734.     while( *txt>= '0' && *txt <='9') {
  735.         num *=10;
  736.         num = num + (*txt -'0');
  737.         txt++;
  738.     }
  739.     return num;
  740. }
  741. //-----------------------------------------------------------------------------
  742. // Find month code from 3-letter string. (Jan,Feb,Mar,Apr,May,...)
  743. //-----------------------------------------------------------------------------
  744. short month_from_str(char *month) {
  745. short code;
  746.  
  747.     code= *month + *(month+1) + *(month+2);        // fold string into unique num
  748.  
  749.     switch (code) {
  750.         case 281: return 0;    case 269: return 1;    case 288: return 2;
  751.         case 291: return 3;    case 295: return 4;    case 301: return 5;
  752.         case 299: return 6;    case 285: return 7;    case 296: return 8;
  753.         case 294: return 9;    case 307: return 10;case 268: return 11;
  754.     }
  755. }
  756. //-----------------------------------------------------------------------------
  757. // Check text for JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC.
  758. //-----------------------------------------------------------------------------
  759. short get_month(void) {
  760.  
  761.     for (i=0; i< NUM_MONTHS; i++) {
  762.         if (is_string(txt, months[i])) {    // check against array of names
  763.             txt += 3;
  764.             return (short) i;
  765.         }
  766.     }
  767.  
  768.     return -1;            // return negative if not found
  769. }
  770. //-----------------------------------------------------------------------------
  771. // Check text for MON, TUE, WED, THU, FRI, SAT, SUN word.
  772. //-----------------------------------------------------------------------------
  773. short get_dayname(void) {
  774.  
  775.     for (i=0; i< NUM_DAYS; i++) {
  776.         if (is_string(txt, days[i])) {
  777.             return (short) i;
  778.         }
  779.     }
  780.  
  781.     return -1;            // return negative if not found
  782. }
  783. //-----------------------------------------------------------------------------
  784. // Compare C-string with embedded word in text.
  785. //-----------------------------------------------------------------------------
  786. BOOL is_string( char * text, char * string) {
  787.  
  788. char *ptr;
  789. char ch;
  790. int result;
  791.  
  792.     ptr = text;                            // remember start of string
  793.  
  794.     ch = *text++;
  795.     while (ch != SPACE && ch != TAB && ch != HYPHEN &&
  796.            ch != COMMA && ch != LF )    // find end of word.
  797.         ch = *text++;
  798.  
  799.     *(text-1) = 0;                        // null-terminate word
  800.     result = strcmp (ptr, string);        // do the comparison
  801.     *(text-1) = ch;                        // put original separator back.
  802.  
  803.     return (BOOL) ! result;
  804. }
  805. //-----------------------------------------------------------------------------
  806. // Printf the LF-terminated line at *txt. (txt is global text pointer)
  807. //-----------------------------------------------------------------------------
  808. void print_line(void) {
  809.  
  810. char *ptr;
  811. char achar;
  812.  
  813.     ptr = txt;
  814.  
  815.     while(*ptr++ != LF) {}
  816.  
  817.     achar = *ptr; *ptr = 0;    printf("%s", txt); *ptr = achar;
  818. }
  819. //-----------------------------------------------------------------------------
  820. // Discard rest of line, incl. LF.
  821. //-----------------------------------------------------------------------------
  822. void skip_line(void){
  823.  
  824.     while (*txt++ != LF) {}
  825. }
  826. //-----------------------------------------------------------------------------
  827. // Skip TABs and SPACEs in buffer.
  828. //-----------------------------------------------------------------------------
  829. void skip_whitespc(void) {
  830.  
  831.     while (*txt == TAB || *txt == SPACE)
  832.         txt++;
  833. }
  834. //-----------------------------------------------------------------------------
  835. // Create day, date and time strings from a DateStamp
  836. //-----------------------------------------------------------------------------
  837. BOOL DStampToStr( struct DateStamp *ds, char *daystr, char *datestr, char *timestr) {
  838.  
  839.     datetime.dat_Stamp    = *ds;                // copy DateStamp struct
  840.  
  841.     datetime.dat_StrDay = daystr;            // set string pointers
  842.     datetime.dat_StrDate = datestr;
  843.     datetime.dat_StrTime = timestr;
  844.  
  845.     datetime.dat_Format = FORMAT_DOS;        // std format
  846.  
  847.     return (BOOL) DateToStr(&datetime);
  848. }
  849. //-----------------------------------------------------------------------------
  850. // Cache a specified file.
  851. // OUTPUTS: file_size, file_buf
  852. //-----------------------------------------------------------------------------
  853. char *load_file( char * filename) {
  854.  
  855. ULONG    bytes;
  856. BPTR    fh;
  857.  
  858.     fh = Open(filename, MODE_OLDFILE);
  859.  
  860.     Seek(fh, 0L, OFFSET_END);                    // Seek to end of file
  861.     bytes = Seek(fh, 0L, OFFSET_BEGINNING);        // figure out size of file
  862.     if (!bytes) {
  863.         printf("File '%s' is empty ! Ouch... (ABORTING)\n", filename);
  864.         exit(10);
  865.     }
  866.  
  867.     file_buf = AllocMem(bytes, MEMF_CLEAR);        // get a buffer for it
  868.  
  869.     if (!file_buf) {
  870.         printf("No buffer for cacheing file '%s'! ABORTING...\n", filename);
  871.         exit(10);
  872.     }
  873.  
  874.     file_size = bytes;
  875.     bytes = Read(fh, file_buf, bytes);
  876.  
  877.     if (bytes != file_size) {
  878.         printf("Read error when reading file '%s'!\n", filename);
  879.         FreeMem(file_buf, file_size);            // free events file buffer
  880.         exit(10);
  881.     }
  882.  
  883.     Close(fh);
  884.  
  885.     return file_buf;
  886. }
  887. //-----------------------------------------------------------------------------
  888. //
  889. //-----------------------------------------------------------------------------
  890. void    mark_file    ( char * file, int position, char * tag) {
  891.  
  892. BPTR    fh;
  893.  
  894.     fh = Open(file, MODE_OLDFILE);
  895.     if (fh) {
  896.         Seek(fh, position, OFFSET_BEGINNING);
  897.         Write(fh, tag, 1);
  898.         Close(fh);
  899.     }
  900. }
  901. //-----------------------------------------------------------------------------
  902. // Popup a nice information requester with a single "OK" button.
  903. //-----------------------------------------------------------------------------
  904.  
  905. char popup_requester( char *msg, char buttons) {
  906.  
  907. int gadg;
  908.  
  909. static struct EasyStruct EventES = {
  910.     sizeof (struct EasyStruct),
  911.     0,
  912.     "TimePlanner Event Notification",
  913.     NULL,
  914.     "OK",
  915. };
  916.  
  917.     EventES.es_GadgetFormat = "OK";                // assume simplest buttons
  918.  
  919.     if (buttons & BUT_CANCEL) {
  920.         EventES.es_GadgetFormat = "Do it.|Skip!";        //
  921.     }
  922.  
  923.     EventES.es_TextFormat = msg;
  924.  
  925.     gadg = EasyRequest(IntuitionBase->ActiveWindow, &EventES, NULL);
  926.     switch (gadg) {
  927.         case 1: return BUT_OK;                        // user clicked on 'OK'
  928.  
  929.         case 0: if (buttons & BUT_CANCEL)
  930.                     return BUT_CANCEL;                // user clicked on 'CANCEL'
  931.                 else
  932.                     return BUT_OK;
  933.     }
  934. }
  935. //-----------------------------------------------------------------------------
  936.